package be.jaisy.adapter.hibernate.extender.internal;

import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.osgi.framework.Bundle;

import be.jaisy.adapter.hibernate.extender.util.ManifestParserHelper;
import be.jaisy.adapter.hibernate.manager.SessionFactoryManager;

/**
 * @author Stany Blanvalet
 */
public class HibernateExtenderRegistry {
	
	private Set<Bundle> activatedBundles;
	
	private SessionFactoryManager sessionFactoryContainer;
	
	public HibernateExtenderRegistry() {
		super();
		this.activatedBundles = new HashSet<Bundle>();
		this.sessionFactoryContainer = SessionFactoryManager.getInstance();
	}

	public void register(Bundle bundle) {
		synchronized(this.activatedBundles) {
			if (activatedBundles.contains(bundle))
				return;
		}
		Map<String, String> sessionFactoryContributions = ManifestParserHelper.getInstance().getSessionFactoryContribution(bundle);
		Map<String, String> dataModelContributions = ManifestParserHelper.getInstance().getDataModelContribution(bundle);
		if(sessionFactoryContributions.isEmpty() && dataModelContributions.isEmpty())
			return;
		
		synchronized(this.activatedBundles) {		
			for (Entry<String, String> entry : sessionFactoryContributions.entrySet()) {							
				try {					
					registerSessionFactory(bundle, entry.getKey(), entry.getValue());
				} catch (Throwable t) {
					Activator.logError(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register SessionFactory ["+entry.getKey()+"] failed :", t);
					continue;
				}
			}
			for (Entry<String, String> entry : dataModelContributions.entrySet()) {							
				try {					
					registerDataModelContribution(bundle, entry.getKey(), entry.getValue());
				} catch (Throwable t) {
					Activator.logError(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register DataModelContribution ["+entry.getKey()+"] failed :", t);
					continue;
				}
			}
			activatedBundles.add(bundle);
		}
	}
	
	public void unregister(Bundle bundle) {
		synchronized(this.activatedBundles) {
			if (!activatedBundles.contains(bundle))
				return;
			activatedBundles.remove(bundle);
		}		
		
		Map<String, String> sessionFactoryContributions = ManifestParserHelper.getInstance().getSessionFactoryContribution(bundle);
		Map<String, String> dataModelContributions = ManifestParserHelper.getInstance().getDataModelContribution(bundle);
		if(sessionFactoryContributions.isEmpty() && dataModelContributions.isEmpty())
			return;

		for (Entry<String, String> entry : sessionFactoryContributions.entrySet()) {							
			try {					
				unRegisterSessionFactory(bundle, entry.getKey(), entry.getValue());
			} catch (Throwable t) {
				Activator.logError(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register SessionFactory ["+entry.getKey()+"] failed :", t);
				continue;
			}
		}
		for (Entry<String, String> entry : dataModelContributions.entrySet()) {							
			try {					
				unRegisterDataModelContribution(bundle, entry.getKey(), entry.getValue());
			} catch (Throwable t) {
				Activator.logError(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register DataModelContribution ["+entry.getKey()+"] failed :", t);
				continue;
			}
		}
	}
	
	public void close() {
		synchronized(this.activatedBundles) {
			for (Bundle bundle : this.activatedBundles) {
				Map<String, String> sessionFactoryContributions = ManifestParserHelper.getInstance().getSessionFactoryContribution(bundle);
				Map<String, String> dataModelContributions = ManifestParserHelper.getInstance().getDataModelContribution(bundle);
				if(sessionFactoryContributions.isEmpty() && dataModelContributions.isEmpty())
					return;

				for (Entry<String, String> entry : sessionFactoryContributions.entrySet()) {							
					try {					
						unRegisterSessionFactory(bundle, entry.getKey(), entry.getValue());
					} catch (Throwable t) {
						Activator.logError(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register SessionFactory ["+entry.getKey()+"] from configuration file ["+entry.getValue()+"] failed :", t);
						continue;
					}
				}
				for (Entry<String, String> entry : dataModelContributions.entrySet()) {							
					try {					
						unRegisterDataModelContribution(bundle, entry.getKey(), entry.getValue());
					} catch (Throwable t) {
						Activator.logError(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register DataModelContribution ["+entry.getKey()+"] to sessionFactory ["+entry.getValue()+"] failed :", t);
						continue;
					}
				}				
			}
			this.activatedBundles.clear();
		}
	}

	private void registerSessionFactory(Bundle bundle, String sessionFactoryId, String sessionFactoryConfigurationFile) throws IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		if(sessionFactoryId==null||sessionFactoryConfigurationFile==null)
			throw new IllegalArgumentException("sessionFactoryId and sessionFactoryConfigurationFile are mandatory");
		URL configurationPath = getResource(bundle, sessionFactoryConfigurationFile);
		if(configurationPath==null)
			throw new IllegalArgumentException("getResource("+sessionFactoryConfigurationFile+") not found");				
		this.sessionFactoryContainer.addSessionFactory(sessionFactoryId, configurationPath);
		Activator.logInfo(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register SessionFactory ["+sessionFactoryId+"] from configuration file ["+sessionFactoryConfigurationFile+"] done.");		
	}
	
	private void registerDataModelContribution(Bundle bundle, String sessionFactoryId, String dataModelClassName) throws IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException{
		if(sessionFactoryId==null||dataModelClassName==null)
			throw new IllegalArgumentException("sessionFactoryId and dataModelClassName are mandatory");
		Class<?> dataModelClass = bundle.loadClass(dataModelClassName);
		if(dataModelClass == null)
			throw new IllegalArgumentException("Cannot load class ["+dataModelClassName+"] from bundle ["+bundle.getBundleId()+"].["+bundle.getSymbolicName()+"]");		
		this.sessionFactoryContainer.addDataModelClass(sessionFactoryId, dataModelClass);
		Activator.logInfo(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] register DataModelContribution ["+dataModelClassName+"] to SessionFactory ["+sessionFactoryId+"] done.");		
	}
	
	private void unRegisterSessionFactory(Bundle bundle, String sessionFactoryId, String sessionFactoryConfigurationFile) throws IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException{
		if(sessionFactoryId==null||sessionFactoryConfigurationFile==null)
			throw new IllegalArgumentException("sessionFactoryId and sessionFactoryConfigurationFile are mandatory");
		this.sessionFactoryContainer.removeSessionFactory(sessionFactoryId);
		Activator.logInfo(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] unregister SessionFactory ["+sessionFactoryId+"] from configuration file ["+sessionFactoryConfigurationFile+"] done.");		
	}
	
	private void unRegisterDataModelContribution(Bundle bundle, String sessionFactoryId, String dataModelClassName) throws IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException{		if(sessionFactoryId==null||dataModelClassName==null)
			throw new IllegalArgumentException("sessionFactoryId and dataModelClassName are mandatory");
		Class<?> dataModelClass = bundle.loadClass(dataModelClassName);
		if(dataModelClass == null)
			throw new IllegalArgumentException("Cannot load class ["+dataModelClassName+"] from bundle ["+bundle.getBundleId()+"].["+bundle.getSymbolicName()+"]");		
		this.sessionFactoryContainer.removeDataModelClass(sessionFactoryId, dataModelClass);
		Activator.logInfo(this.getClass(),"bundle ["+bundle.getBundleId()+"].["+ bundle.getSymbolicName()+"] unregister DataModelContribution ["+dataModelClassName+"] to SessionFactory ["+sessionFactoryId+"] done.");		
	}
	
	private static URL getResource(Bundle bundle, String resourceName) {
		int lastSlash = resourceName.lastIndexOf('/');
		if (lastSlash == -1) return null;		
		String path = resourceName.substring(0, lastSlash);
		if (path.length() == 0)	path = "/"; //$NON-NLS-1$
		String file = resourceName.substring(lastSlash + 1);
		Enumeration entryPaths = bundle.findEntries(path, file, false);
		
		URL result = null;
		if (entryPaths != null && entryPaths.hasMoreElements()){
			result = (URL) entryPaths.nextElement();
			return result; 
		}
		return null;
	}
}